import { Callout } from '@/components'

# Migrating to v3

## What's new

### New `mutationOptions` [#1490](https://github.com/toss/suspensive/pull/1490)

`mutationOptions` helps you easily reuse and consistently manage option objects for Mutations. This provides similar benefits to the ones offered by [`queryOptions`](/docs/react-query/queryOptions).

```jsx /mutationOptions/
import { mutationOptions, useMutation, Mutation } from '@suspensive/react-query'

const editPostMutationOptions = (postId: number) =>
  mutationOptions({
    mutationFn: (content: string) => fetch(`https://example.com/posts/${postId}`, {
      method: 'PATCH',
      body: JSON.stringify({ content }),
    }).then(res => res.json()),
  })

// You can directly use mutationOptions without creating custom mutation hooks.
const editPostMutation = useMutation(editPostMutationOptions(1))

// Directly use mutationOptions with <Mutation />.
const Example = () => (
  <Mutation {...editPostMutationOptions(1)}>
    {({ mutate, isLoading }) => (
      <div>
        <p>{isLoading ? 'Updating...' : 'Latest updated'}</p>
        <textarea onBlur={(e) => mutate(e.target.value)} disabled={isLoading} />
      </div>
    )}
  </Mutation>
)
```

## Handling BREAKING CHANGES

### `networkMode` is now fixed to `'always'` in `useSuspenseQuery`, `useSuspenseQueries`, `useSuspenseInfiniteQuery` of `@suspensive/react-query` [#1458](https://github.com/toss/suspensive/pull/1458)

<Callout type='info'>

This breaking change affects users who use both `@tanstack/react-query` v4 and `@suspensive/react-query`.

</Callout>

Previously, online status was determined using the `navigator.onLine` API. However, Chromium-based browsers have a known issue where this API may return incorrect values, falsely reporting the device as offline even when it is connected.  
To ensure consistent behavior across all environments, `@suspensive/react-query` now sets the default `networkMode` to `'always'` when using Suspense hooks.

This change aligns with the default behavior in React Query v5 and prevents Suspense hooks from starting with `fetchStatus: 'paused'` during runtime.

- Reference: [Default networkMode configuration and fetchStatus = "paused"](https://github.com/TanStack/query/discussions/4753), [navigator.onLine returning false value even network is available](https://issues.chromium.org/issues/338514113)

```diff
import { queryOptions, useSuspenseQuery } from '@suspensive/react-query'

// Applies the same to queryOptions and infiniteQueryOptions as well.
const QueryOptionsExample = () => {
  const options = queryOptions({
    queryKey: ['key'],
    queryFn: () => api()
-   networkMode: 'always'
  })
}

// Applies the same to useSuspenseQuery, useSuspenseInfiniteQuery, SuspenseQuery, and SuspenseInfiniteQuery.
const SuspenseQueryExample = () => {
  const query = useSuspenseQuery({
    queryKey: ['key'],
    queryFn: () => api()
-   networkMode: 'always'
  })
}
```

### `<QueryErrorBoundary/>` is removed [#1424](https://github.com/toss/suspensive/pull/1424)

In `@suspensive/react-query` v2, `QueryErrorBoundary` was provided as a peer dependency of `@suspensive/react`.
However, this approach complicated user dependencies for the sake of a simple implementation.
Therefore, it has been removed to simplify the library.

You can implement your own `QueryErrorBoundary` as shown below.

```diff
- import { QueryErrorBoundary } from '@suspensive/react-query'
+ import { ErrorBoundary } from '@suspensive/react'
+ import { useQueryErrorResetBoundary } from '@tanstack/react-query'
+ import {
+   type ComponentPropsWithoutRef,
+   type ComponentRef,
+   forwardRef,
+ } from 'react'

+ const QueryErrorBoundary = forwardRef<
+   ComponentRef<typeof ErrorBoundary>,
+   ComponentPropsWithoutRef<typeof ErrorBoundary>
+ >(({ onReset, ...props }, resetRef) => {
+   const { reset } = useQueryErrorResetBoundary()
+   return (
+     <ErrorBoundary
+       {...props}
+       onReset={() => {
+         onReset?.()
+         reset()
+       }}
+       ref={resetRef}
+     />
+   )
+ })

const Example = () => (
  <QueryErrorBoundary>
    <SuspenseQuery />
  </QueryErrorBoundary>
)
```
